#ifndef _COMMON_H
#define _COMMON_H
#include <iostream>
#include <sys/time.h>
#include <string.h>
#include <cuda_runtime.h>
#define GPU_BLOCK_THREADS 512 // 512=32*16

#define CHECK(call)                                                          \
    {                                                                        \
        const cudaError_t error = call;                                      \
        if (error != cudaSuccess)                                            \
        {                                                                    \
            printf("ERROR: %s:%d,", __FILE__, __LINE__);                     \
            printf("code:%d,reason:%s\n", error, cudaGetErrorString(error)); \
            exit(1);                                                         \
        }                                                                    \
    }

#define CHECK_CUBLAS(call)                                                   \
    {                                                                        \
        cublasStatus_t err;                                                  \
        if ((err = (call)) != CUBLAS_STATUS_SUCCESS)                         \
        {                                                                    \
            fprintf(stderr, "Got CUBLAS error %d at %s:%d\n", err, __FILE__, \
                    __LINE__);                                               \
            exit(1);                                                         \
        }                                                                    \
    }

template <typename T>
void initialDataRandom(T *ip, int size)
{
    time_t t;
    srand((unsigned)time(&t));
    for (int i = 0; i < size; i++)
    {
        ip[i] = T(rand() & 0xff);
    }
}

template <typename T>
void initialDataConstValue(T *ip, int size, int value)
{
    time_t t;
    srand((unsigned)time(&t));
    for (int i = 0; i < size; i++)
    {
        ip[i] = T(value);
    }
}

template <typename T>
void initialDataIncreasing(T *ip, int size)
{
    time_t t;
    srand((unsigned)time(&t));
    for (int i = 0; i < size; i++)
    {
        ip[i] = T(i + 1);
    }
}

template <typename T>
void printVector(T *ip, const int size, const char *name = "")
{
    std::cout << "vector " << name << " print:"
              << "  ";
    for (int i = 0; i < size; i++)
        std::cout << ip[i] << " ";
    std::cout << std::endl;
}

template <typename T>
void printMatrix(T *ip, const int m, const int n, const char *name = "")
{
    std::cout << "Matrix " << name << " print:" << std::endl;
    for (int i = 0; i < m; i++)
    {
        std::cout << "   ";
        for (int j = 0; j < n; j++)
            std::cout << ip[j + i * n] << " ";
        std::cout << std::endl;
    }
}

template <typename T>
void checkResult(T *hostRef, T *gpuRef, const int N)
{
    double epsilon = 1.0E-8;
    for (int i = 0; i < N; i++)
    {
        if (abs(hostRef[i] - gpuRef[i]) > epsilon)
        {
            std::cout << "Results don\'t match!" << std::endl;
            std::cout << hostRef[i] << "(cpu) != " << gpuRef[i] << "(gpu)"
                      << "|index=" << i << std::endl;
            return;
        }
    }
    std::cout << "Check result success!" << std::endl;
}

inline double get_time()
{
    struct timeval tp;
    struct timezone tzp;
    int i = gettimeofday(&tp, &tzp);
    return ((double)tp.tv_sec + (double)tp.tv_usec * 1.e-6) * 1000;
}

dim3 grid_dims(int numJobs)
{
    int numBlockThreads = numJobs < GPU_BLOCK_THREADS ? numJobs : GPU_BLOCK_THREADS;
    return dim3((numJobs - 1) / numBlockThreads + 1); // 其实就是num_threads/block.x的向上取整的一种写法
}

dim3 block_dims(int numJobs)
{
    return numJobs < GPU_BLOCK_THREADS ? numJobs : GPU_BLOCK_THREADS;
}

#endif // _COMMON_H